package com.cocotingo.snail;

import com.cocotingo.snail.dispatcher.Dispatcher;


abstract public class Animation {
	
	private final float duration;
	private final AnimationFunction function;
	private float timer;
	private boolean playing;
	private final View target;
	
	public Animation(float duration) {
		this(duration, null, null);
	}
	
	public Animation(float duration, View target) {
		this(duration, null, target);
	}
	
	public Animation(float duration, AnimationFunction function) {
		this(duration, function, null);
	}
	
	public Animation(float duration, AnimationFunction function, View target) {
		this.duration = duration;
		this.function = function == null ? AnimationFunction.LINEAR : function;
		this.target = target;
		timer = 0.0f;
		playing = false;
	}
	
	public boolean isPlaying() {
		return playing;
	}
	
	public float getDuration() {
		return duration;
	}
	
	public AnimationFunction getFunction() {
		return function;
	}
	
	public void commit() {
		if (!playing) {
			timer = 0.0f;
			ViewContext.getDriver().registerAnimation(this);
			playing = true;
			animation(function.apply(0.0f));
		} else {
			throw new IllegalStateException("illegal animation state: the animation is already committed.");
		}
	}
	
	public void interrupt() {
		if (playing) {
			playing = false;
			ViewContext.getDriver().unregisterAnimation(this);
			Dispatcher.executeAndSyncWithGUI(new Runnable() {
				@Override
				public void run() {
					completed(true);
				}
			});
		} else {
			throw new IllegalStateException("illegal animation state: the animation is not playing.");
		}
	}
	
	final void update(float dt) {
		if (!playing) return;
		
		timer += dt;
		if (timer < duration) {
			animation(function.apply(timer / duration));
		} else {
			animation(function.apply(1.0f));
			playing = false;
			ViewContext.getDriver().unregisterAnimation(this);
			completed(false);
		}
	}
	
	abstract protected void animation(float progress);
	
	protected void completed(boolean canceled) {
	}
	
	protected float getLinearProgress() {
		return timer / duration;
	}
	
	public View getTarget() {
		return target;
	}
	
}
